
#pragma once


#include <string.h>
#include <stdlib.h>

template<typename T>
class CHMAC
{
private:
    T outer;
    T inner;

public:
    static const size_t OUTPUT_SIZE = T::OUTPUT_HMAC_SIZE;
    static const size_t BLOCK_SIZE = T::BLOCK_SIZE;

    CHMAC(const unsigned char* key, size_t keylen) 
    {
        unsigned char rkey[BLOCK_SIZE] = {};
        if (keylen <= BLOCK_SIZE) {
            memcpy(rkey, key, keylen);
        }
        else {
            T().Write(key, keylen).Finalize(rkey);
        }

        for (int n = 0; n < BLOCK_SIZE; n++)
            rkey[n] ^= 0x5c;
        outer.Write(rkey, BLOCK_SIZE);

        for (int n = 0; n < BLOCK_SIZE; n++)
            rkey[n] ^= 0x5c ^ 0x36;
        inner.Write(rkey, BLOCK_SIZE);
    }

    CHMAC& Write(const unsigned char* data, size_t len)
    {
        inner.Write(data, len);
        return *this;
    }

    void Finalize(unsigned char hash[OUTPUT_SIZE]) 
    {
        unsigned char temp[OUTPUT_SIZE];
        inner.Finalize(temp);
        outer.Write(temp, OUTPUT_SIZE).Finalize(hash);
    }
};